#include "test_tcfixedbuffer_functional.h"

using namespace PILO::Core::String;
using namespace PILO::Core::Memory;

static INT32 g_id = 0;

static INT32 test_TCFixedBuffer_Constructor(void * param);
static INT32 test_TCFixedBuffer_write(void* param);
static INT32 test_TCFixedRingBuffer_read(void* param);
static INT32 test_TCFixedBuffer_shrink(void* param);
static INT32 test_TCFixedBuffer_attach(void* param);
static INT32 test_TCFixedBuffer_peek(void* param);

static case_testing_t g_cases_TCFixedBuffer[] = {
    {1, "TCFixedBuffer constructor",                 0,test_TCFixedBuffer_Constructor,             -1},
    {2, "write()                  ",                 0,test_TCFixedBuffer_write,                   -1},
    {3, "read()                   ",                 0,test_TCFixedRingBuffer_read,                    -1},
    {4, "shrink()                 ",                 0,test_TCFixedBuffer_shrink,                  -1},
    {5, "attach()                 ",                 0,test_TCFixedBuffer_attach,                  -1},
    {6, "peek()                   ",                 0,test_TCFixedBuffer_peek,                    -1},
};





INT32 test_TCFixedBuffer_Constructor(void * )
{
    TCFixedBuffer<char,2048>  fixedBuffer_0;
    //fixedBuffer_0.debugInternalDataToConsole();

    if (fixedBuffer_0.internalBufferLength() != 2048) {
        return -1;
    }

    if (fixedBuffer_0.readAwailable() != 0) {
        return -2;
    }

    if (fixedBuffer_0.writeAwaiable() != 2048) {
        return -3;
    }

    if (fixedBuffer_0.dataSize() != 0) {
        return -4;
    }

    if (fixedBuffer_0.elementSize() != sizeof(char)) {
        return -5;
    }

    ValidateBufferObjectInformation(fixedBuffer_0, 2048, 0, MC_INVALID_SIZE, 0, 0, 2048);

    TCFixedBuffer<TestElement, 4096>    fixedBuffer_1;
    if (fixedBuffer_1.internalBufferLength() != 4096) {
        return -6;
    }

    if (fixedBuffer_1.readAwailable() != 0) {
        return -7;
    }

    if (fixedBuffer_1.writeAwaiable() != 4096) {
        return -8;
    }

    if (fixedBuffer_1.dataSize() != 0) {
        return -9;
    }

    if (fixedBuffer_1.elementSize() != sizeof(TestElement)) {
        return -10;
    }

    BOOL raisedExp = FALSE;
    try {
        TCFixedBuffer<char, MC_LIMIT_TCFIXEDBUFFER_MAX_ELEMENT_NUMBER + 1> invalidSizeBuffer;
    } catch (INT32 i) {
        if (i == EC_REACH_UPPER_LIMIT) 
        {
            raisedExp = TRUE;
        }
    }
    if (! raisedExp) {
        return FALSE;
    }
    

    return 0;
}

INT32 test_TCFixedBuffer_write(void* )
{
    //testing write function
    TCFixedBuffer<char,2048>  fixedBuffer_0;
    if (fixedBuffer_0.write("0123456789",10) != 10) {
        return -1;
    }
    //fixedBuffer_0.debugInternalDataToConsole();
    if (fixedBuffer_0.readAwailable() != 10) {
        return -2;
    }
    if (fixedBuffer_0.dataSize() != 10) {
        return -3;
    }
    if (fixedBuffer_0.writeAwaiable() != 2038) {
        return -4;
    }

    // testing flag EnumWaitAll
    char buf[2038];
    if (fixedBuffer_0.write(buf, 2039, Buffer::EnumNoopOnPartial) != -1) {
        return -5;
    }

    if (fixedBuffer_0.write(buf, 2039) != 2038) {
        return -6;
    }

    // verify data
    fixedBuffer_0.reset();
    if (fixedBuffer_0.write("0123456789",10) != 10) {
        return -7;
    }
    if (fixedBuffer_0.write("0123456789",10) != 10) {
        return -8;
    }
    if (memcmp(fixedBuffer_0.dataForReading(), "01234567890123456789", 20) != 0) {
        return -9;
    }
    fixedBuffer_0.reset();
    char buff[2048];
    memset(buff, 0x1A, sizeof(buff));
    char c = 0x1A;
    for (int i = 0; i < 2048; i ++) {
        fixedBuffer_0.write(&c, 1);
    }
    if (memcmp(fixedBuffer_0.dataForReading(), buff, sizeof(buff)) != 0) {
        return -10;
    }

    if (fixedBuffer_0.read(buff, 500) != 500) {
        return -11;
    }

    if (fixedBuffer_0.readAwailable() != (2048 - 500)) {
        return -12;
    }

    if (fixedBuffer_0.writeAwaiable() != 0) {
        return -13;
    }

    // test write index after read, actually this is testing writeAvailable()


    TCFixedBuffer<TestElement,1024>  fixedBuffer_1;
    TestElement data[1000];
    for (int i = 0; i < 1000; i++) {
        data[i].setID(i);
    }

    if (fixedBuffer_1.write(data,1000) != 1000) {
        return -101;
    }
    if (fixedBuffer_1.readAwailable() != 1000) {
        return -102;
    }
    if (fixedBuffer_1.dataSize() != 1000*sizeof(TestElement)) {
        return -103;
    }
    if (fixedBuffer_1.writeAwaiable() != 24) {
        return -104;
    }

    // testing flag EnumNoopOnPartial
    TestElement te[50];
    if (fixedBuffer_1.write(te, 25, Buffer::EnumNoopOnPartial) != -1) {
        return -105;
    }

    if (fixedBuffer_1.write(te, 25) != 24) {
        return -106;
    }
    // verify data
    fixedBuffer_1.reset();
    TestElement te2[512];
    for (int i = 0; i < 512; i++) {
        te2[i].setID(i);
        te2[i].setData(i);
    }
    if (fixedBuffer_1.write(te2, 200) != 200) {
        return -107;
    }
    if (fixedBuffer_1.write(te2+200, 312) != 312) {
        return -108;
    }
    for (int i = 0; i < 512; i++) {
        TestElement* ptrTE = (TestElement*) fixedBuffer_1.dataForReading() + i;
        if (ptrTE == 0) {
            return -109;
        }
        if (ptrTE->id() != i) {
            return -110;
        }

        char tmpBuff[8];
        FormatOutputToString(tmpBuff, sizeof(tmpBuff), "%06d", i);
        if (memcmp(ptrTE->data(), tmpBuff, 6) != 0) {
            return -111;
        }
    }

    if (fixedBuffer_1.readAwailable() != 512) {
        return -112;
    }

    if (fixedBuffer_1.writeAwaiable() != 512) {
        return -113;
    }

    if (fixedBuffer_1.read(te2, 500) != 500) {
        return -114;
    }

    if (fixedBuffer_1.readAwailable() != (512 - 500)) {
        return -115;
    }

    if (fixedBuffer_1.writeAwaiable() != 512) {
        return -116;
    }

    return 0;
}


INT32 test_TCFixedRingBuffer_read(void* )
{
    //testing char buffer
    TCFixedBuffer<char, 256>   fixedBuffer_0;
    for(int i=0; i <256; i++) {
        char c = (char) i;
        if (fixedBuffer_0.write(&c,1) != 1) {
            return -1;
        }
    }

    char buf[256];
    if (fixedBuffer_0.read(buf, 0) != 0) {
        return -2;
    }
    if (fixedBuffer_0.read(buf, 1000, Buffer::EnumNoopOnPartial) != -1) {
        return -3;
    }
    if (fixedBuffer_0.read(buf, 100) != 100) {
        return -4;
    }
    if (fixedBuffer_0.read(buf, 156) != 156) {
        return -5;
    }
    if (fixedBuffer_0.read(buf, 1) != 0) {
        return -6;
    }

    //testing class object buffer
    TCFixedBuffer<TestElement,256>  fixedBuffer_1;
    TestElement data[256];
    for (int i = 0; i < 256; i++) {
        data[i].setID(i);
    }
    if (fixedBuffer_1.write(data, 256) != 256) {
        return -101;
    }
    if (fixedBuffer_1.readAwailable() != 256) {
        return -102;
    }
    TestElement readBuff[256];
    if (fixedBuffer_1.read(readBuff, 0) != 0) {
        return -103;
    }
    if (fixedBuffer_1.read(readBuff, 257, Buffer::EnumNoopOnPartial) != -1) {
        return -104;
    }
    if (fixedBuffer_1.read(readBuff, 100) != 100) {
        return -105;
    }
    for (int i = 0; i < 100; i ++) {
        if (readBuff[i].id() != i) {
            return -106;
        }
    }
    if (fixedBuffer_1.read(readBuff+100, 156) != 156) {
        return -107;
    }
    for (int i = 100; i < 256; i ++) {
        if (readBuff[i].id() != i) {
            return -108;
        }
    }
    if (fixedBuffer_1.read(readBuff, 1) != 0) {
        return -109;
    }

    //fixedBuffer_1.debugObjectInformationToConsole();

    return 0;
}

INT32 test_TCFixedBuffer_shrink(void* )
{
    //testing char buffer
    TCFixedBuffer<char, 256>   fixedBuffer_0;
    for(int i=0; i <256; i++) {
        char c = (char) i;
        if (fixedBuffer_0.write(&c,1) != 1) {
            return -1;
        }
    }

    char buf[256];
    memset(buf,0x00,sizeof(buf));
    if (fixedBuffer_0.read(buf, 47) != 47)
    {
        return -2;
    }
    //fixedBuffer_0.debugObjectInformationToConsole();
    if (fixedBuffer_0.read(buf+47, 53) != 53) {
        return -3;
    }

    for (int i = 0; i < 100; i++) {
        if ((UINT8) buf[i] != i) {
            return -4;
        }
    }

    fixedBuffer_0.shrink();
    if (fixedBuffer_0.indexForReading() != 0) {
        return -5;
    }
    if (fixedBuffer_0.indexForWriting() != 256 - 100) {
        return -6;
    }

    // continue reading, empty this buffer
    fixedBuffer_0.read(buf + 100, 156);
    for (int i = 100; i < 256; i++) {
        if ((UINT8) buf[i] != i) {
            return -7;
        }
    }

    fixedBuffer_0.shrink();
    if (fixedBuffer_0.indexForReading() != MC_INVALID_SIZE) {
        return -8;
    }
    if (fixedBuffer_0.indexForWriting() != 0) {
        return -9;
    }

    // testing shrink empty
    fixedBuffer_0.shrink();
    if (fixedBuffer_0.indexForReading() != MC_INVALID_SIZE) {
        return -10;
    }
    if (fixedBuffer_0.indexForWriting() != 0) {
        return -11;
    }

    // testing element of complicate type
    TCFixedBuffer<TestElement, 256>   fixedBuffer_1;
    for(int i=0; i <256; i++) {
        TestElement t;
        t.setID(i);
        t.setData(i);
        if (fixedBuffer_1.write(&t,1) != 1) {
            return -101;
        }
    }

    TestElement eleBuff[256];
    memset(eleBuff,0x00,sizeof(eleBuff));
    if (fixedBuffer_1.read(eleBuff, 47) != 47) {
        return -102;
    }
    //fixedBuffer_0.debugObjectInformationToConsole();
    if (fixedBuffer_1.read(eleBuff+47, 53) != 53) {
        return -103;
    }
    for (int i = 0; i < 100; i++) {
        if ((UINT8) eleBuff[i].id() != i) {
            return -104;
        }
    }

    fixedBuffer_1.shrink();
    if (fixedBuffer_1.indexForReading() != 0) {
        return -105;
    }
    if (fixedBuffer_1.indexForWriting() != 256 - 100) {
        return -106;
    }
    // continue reading, empty this buffer
    fixedBuffer_1.read(eleBuff + 100, 156);
    for (int i = 100; i < 256; i++) {
        if ( eleBuff[i].id()!= i) {
            return -107;
        }
    }

    fixedBuffer_1.shrink();
    if (fixedBuffer_1.indexForReading() != MC_INVALID_SIZE) {
        return -108;
    }
    if (fixedBuffer_1.indexForWriting() != 0) {
        return -108;
    }

    // testing shrink empty
    fixedBuffer_1.shrink();
    if (fixedBuffer_1.indexForReading() != MC_INVALID_SIZE) {
        return -110;
    }
    if (fixedBuffer_1.indexForWriting() != 0) {
        return -111;
    }

    return 0;
}


INT32 test_TCFixedBuffer_attach(void* )
{
    
    // char buffer
    TCFixedBuffer<char, 256> fixedBuffer_0;
    char cBuffToAttach[1024];

    //attaching
    fixedBuffer_0.attach(cBuffToAttach, 300, 100, 200);
    if (fixedBuffer_0.internalBufferLength() != 300) {
        return -1;
    }
    if (ValidateBufferObjectInformation(fixedBuffer_0, 300, 200, 100, 200, MC_INVALID_SIZE, 0) != 0) {
        return -2;
    }

    fixedBuffer_0.attach(cBuffToAttach, 300, 10, 200);
    if (ValidateBufferObjectInformation(fixedBuffer_0, 300, 200, 10, 200, 210, 90) != 0) {
        return -3;
    }

    // testing read after attached
    char buf[256];

    if (fixedBuffer_0.read(buf, 0) != 0) {
        return -4;
    }
    if (fixedBuffer_0.read(buf, 1000, Buffer::EnumNoopOnPartial) != -1) {
        return -5;
    }
    if (fixedBuffer_0.read(buf, 100) != 100) {
        return -6;
    }
    if (fixedBuffer_0.read(buf, 100) != 100) {
        return -7;
    }
    if (fixedBuffer_0.read(buf, 1) != 0) {
        return -8;
    }

    // testing write
    fixedBuffer_0.reset();
    if (ValidateBufferObjectInformation(fixedBuffer_0, 300, 0, MC_INVALID_SIZE, 0, 0, 300) != 0) {
        return -9;
    }

    for (int i = 0; i< 256; i ++) {
        buf[i] = (BYTE) i;
    }

    if (fixedBuffer_0.write("0123456789",10) != 10) {
        return -10;
    }
    //fixedBuffer_0.debugInternalDataToConsole();
    if (fixedBuffer_0.readAwailable() != 10) {
        return -11;
    }
    if (fixedBuffer_0.dataSize() != 10) {
        return -12;
    }
    if (ValidateBufferObjectInformation(fixedBuffer_0, 300, 10, 0, 10, 10, 290) != 0) {
        return -13;
    }
    if (fixedBuffer_0.write(buf, 400, Buffer::EnumNoopOnPartial) != -1) {
        return -14;
    }
    if (fixedBuffer_0.write(buf, 256, Buffer::EnumNoopOnPartial) != 256) {
        return -14;
    }
    if (ValidateBufferObjectInformation(fixedBuffer_0, 300, 266, 0, 266, 266, 34) != 0) {
        return -13;
    }
    
    for (int i = 0; i< 10; i ++) {
        if (fixedBuffer_0.internalBuffer()[i] != 0x30 + i) {
            return -15;
        }
    }
    for (int i = 10; i < 266; i ++) {
        int c = (BYTE) fixedBuffer_0[i];
        if (c != i-10) {
            return -16;
        }
    } 
    
    // object buffer
    TCFixedBuffer<TestElement, 256> fixedBuffer_1;
    TestElement oBuffToAttach[1024];
    for (int i = 0; i < 1024; i ++) {
        oBuffToAttach[i].setID(i);
    }
    fixedBuffer_1.attach(oBuffToAttach, 300, 100, 35);
    if (ValidateBufferObjectInformation(fixedBuffer_1, 300, 35*sizeof(TestElement), 100, 35, 135, 165) != 0) { 
        return -101;
    }

    fixedBuffer_1.attach(oBuffToAttach, 500, 10, 350);
    if (ValidateBufferObjectInformation(fixedBuffer_1, 500, 350*sizeof(TestElement), 10, 350, 360, 140) != 0) { 
        return -102;
    }

    TestElement readElementBuffer[500];

    if (fixedBuffer_1.read(readElementBuffer, 0) != 0) {
        return -103;
    }
    if (fixedBuffer_1.read(readElementBuffer, 351, Buffer::EnumNoopOnPartial) != -1) {
        return -104;
    }
    if (fixedBuffer_1.read(readElementBuffer, 100) != 100) {
        return -105;
    }
    if (fixedBuffer_1.read(readElementBuffer+100, 250) != 250) {
        return -106;
    }
    if (fixedBuffer_1.read(readElementBuffer+350, 1) != 0) {
        return -107;
    }
    if (fixedBuffer_1.read(readElementBuffer+350, 1, Buffer::EnumNoopOnPartial) != -1) {
        return -108;
    }

    for (int i = 0; i< 350; i++) {
        if (readElementBuffer[i].id() != i + 10) {
            return -109;
        }
    }

    // testing writing
    fixedBuffer_1.reset();
    TestElement te2[500];
    for (int i = 0; i < 500; i++) {
        te2[i].setID(i);
        te2[i].setData(i);
    }
    if (fixedBuffer_1.write(te2, 200) != 200) {
        return -110;
    }
    if (fixedBuffer_1.write(te2+200, 300) != 300) {
        return -111;
    }
    if (fixedBuffer_1.write(te2, 1, Buffer::EnumNoopOnPartial) != -1) {
        return -112;
    }

    for (int i = 0; i < 500; i++) {
        TestElement* ptrTE = (TestElement*) fixedBuffer_1.dataForReading() + i;
        if (ptrTE == 0) {
            return -113;
        }
        if (ptrTE->id() != i) {
            return -114;
        }

        char tmpBuff[8];
        FormatOutputToString(tmpBuff, sizeof(tmpBuff), "%06d", i);
        if (memcmp(ptrTE->data(), tmpBuff, 6) != 0) {
            return -115;
        }
    }

    if (ValidateBufferObjectInformation(fixedBuffer_1, 500, 500*sizeof(TestElement), 0, 500, MC_INVALID_SIZE, 0) != 0){
        return -116;
    }

    //additional test: test parameters of attach
    TCFixedBuffer<TestElement, 10> fixedBuffer_b;
    TestElement teb[10];
    fixedBuffer_b.attach(teb,10, 5, 0);
    if (ValidateBufferObjectInformation(fixedBuffer_b, 10, 0,MC_INVALID_SIZE , 0, 0, 10) != 0) {
        return -117;
    }

    BOOL expRaised = FALSE;
    char buff[4097];
    TCFixedBuffer<char, 4000> errorBuffer;
    try {
        errorBuffer.attach(buff, sizeof(buff)/sizeof(char), 0,0);
    } catch (INT32 i) {
        if (i == EC_REACH_UPPER_LIMIT) {
            expRaised = TRUE;
        }
    }
    if (expRaised == FALSE) {
        return -118;
    }
    
    return 0;
}



INT32 test_TCFixedBuffer_peek( void*  )
{
    TCFixedBuffer<char, 256> rBuffer_0;

    char sampleBuffer[256];
    for (int i = 0; i<sizeof(sampleBuffer); i ++) {
        sampleBuffer[i] = (char) i;
    }

    if (256 != rBuffer_0.write(sampleBuffer, 256)) {
        return -1;
    }

    if (0 !=ValidateBufferObjectInformation(rBuffer_0, 256, 256, 0, 256, MC_INVALID_SIZE, 0)) {
        return -2;
    }

    char buffer[256];
    if (100 != rBuffer_0.peek(buffer, 100)) {
        return -3;
    }

    if (0 !=ValidateBufferObjectInformation(rBuffer_0, 256, 256, 0, 256, MC_INVALID_SIZE, 0)) {
        return -4;
    }

    if (MC_INVALID_SIZE != rBuffer_0.peek(buffer, 257, Buffer::EnumNoopOnPartial)) {
        return -5;
    }

    if (0 !=ValidateBufferObjectInformation(rBuffer_0, 256, 256, 0, 256, MC_INVALID_SIZE, 0)) {
        return -6;
    }

    // complex objects
    TCFixedBuffer<TestElement, 256> rBuffer_1;
    TestElement sampleBuffer1[256];
    for (int i = 0; i < 256; i ++) {
        sampleBuffer1[i].setID(i);
        sampleBuffer1[i].setData(i);
    }

    if (256 != rBuffer_1.write(sampleBuffer1, 256)) {
        return -101;
    }

    if (0 !=ValidateBufferObjectInformation(rBuffer_1, 256, 256 * sizeof(TestElement), 0, 256, MC_INVALID_SIZE, 0)) {
        return -102;
    }

    TestElement buffer2[256];
    if (100 != rBuffer_1.peek(buffer2, 100)) {
        return -103;
    }

    if (0 !=ValidateBufferObjectInformation(rBuffer_1, 256, 256 * sizeof(TestElement), 0, 256, MC_INVALID_SIZE, 0)) {
        return -104;
    }

    if (MC_INVALID_SIZE != rBuffer_1.peek(buffer2, 257, Buffer::EnumNoopOnPartial)) {
        return -105;
    }

    if (0 !=ValidateBufferObjectInformation(rBuffer_1, 256, 256 * sizeof(TestElement), 0, 256, MC_INVALID_SIZE, 0)) {
        return -106;
    }

    return 0;
}


//------------------------------------------- main test function -------------------------------------------------

INT32 testTCFixedBufferFunctional(INT32 id, int breakOnErrorOccured)
{
    g_id = id;

    FormatOutputToConsole("Testing Part_%d <tcfixedbuffer.h>\n", id);

    int i=0;
    for (i=0; i<MCF_COUNT_OF(g_cases_TCFixedBuffer); i++) {
        case_testing_t * caseParam = &g_cases_TCFixedBuffer[i];
        ASSERT(caseParam != NULL);
        FormatOutputToConsole(" #[%03d.%03d]: Testing %s \t",g_id, caseParam->ct_id, caseParam->ct_decription);

        caseParam->ct_result = caseParam->ct_function(static_cast<case_testing_t *>(caseParam));
        if (caseParam->ct_result == 0) { //ok
            FormatOutputToConsole("[PASSED]\n");
        } else {
            FormatOutputToConsole("[FAILED:%d]\n",caseParam->ct_result);
            if (breakOnErrorOccured) {
                ASSERT(FALSE);
            }
        }
    }


    FormatOutputToConsole("Result of Part_%d <tcfixedbuffer.h/cpp> total  %d, failed %d\n\n",id, MCF_COUNT_OF(g_cases_TCFixedBuffer), failedOFCases(g_cases_TCFixedBuffer,MCF_COUNT_OF(g_cases_TCFixedBuffer)));
    return 0;
}
